package com.dny.asmtop;

import jdk.internal.org.objectweb.asm.ClassWriter;

/**
 * Created by jlutt on 2018-01-15.
 * <br>
 * 拼接字节码
 * <br>
 * 栈映射帧
 * <p>
 * ClassWriter.COMPUTE_MAXS 局部变量和操作数栈的大小就会自动计算。
 * 但是，仍然需要自己调用visitMaxs方法，尽管可以使用任何参数：实际上这些参数会被忽略，然后重新计算。使用这个选项，仍然需要计算帧的大小
 * <p>
 * ClassWriter.COMPUTE_FRAMES 所有的大小都将自动计算。也不许要调用visitFrame方法，
 * 但是仍然需要调用visitMaxs方法（参数将被忽略然后重新计算）
 * 使用这些选项是方便很多，但是会带来一些损失：COMPUTE_MAXS选项将使得ClassWriter慢10%，使用COMPUTE_FRAMES选项将使得ClassWriter慢两倍。你必须将它与自己计算花费的时间相比较：与ASM提供的算法相比较，在某些特定的情况下，这里有一些更容易和快速的算法来进行计算，但是必须处理所有的情形。
 * <p>
 * 如果你选择自己计算这些帧的大小，你可以让ClassWriter来帮你进行帧压缩。这样的话，你只能使用visitFrame(F_NEW,nLocals,locals,nStack,stack)方法去访问那些未被压缩的帧,nLocals和nStack代表局部变量和操作数栈的大小，locals和stack是一些包含对应类型的数组（参看JavaDoc）。
 * <p>
 * 注意，为了自动计算帧的大小，有时必须计算两个类共同的父类。缺省情况下，ClassWriter将会在getCommonSuperClass方法中计算这些，通过在加载这两个类进入虚拟机时，使用反射API来计算。但是，如果你将要生成的几个类相互之间引用，这将会带来问题，因为引用的类可能还不存在。在这种情况下，你可以重写getCommonSuperClass方法来解决这个问题
 *
 * @author jlutt
 */
public final class ASMClassWriter extends ClassWriter {

  private final ASMClassLoader classLoader;

  private ASMClassWriter(ASMClassLoader classLoader) {
    super(ClassWriter.COMPUTE_FRAMES);
    this.classLoader = classLoader;
  }

  public static ASMClassWriter create(ASMClassLoader classLoader) {
    return new ASMClassWriter(classLoader);
  }

  @Override
  protected String getCommonSuperClass(String type1, String type2) {
    Class<?> c, d;
    try {
      c = Class.forName(type1.replace('/', '.'), false, classLoader);
      d = Class.forName(type2.replace('/', '.'), false, classLoader);
    } catch (Exception e) {
      throw new RuntimeException(e.toString());
    }
    if (c.isAssignableFrom(d)) {
      return type1;
    }
    if (d.isAssignableFrom(c)) {
      return type2;
    }
    if (c.isInterface() || d.isInterface()) {
      return "java/lang/Object";
    } else {
      do {
        c = c.getSuperclass();
      } while (!c.isAssignableFrom(d));
      return c.getName().replace('.', '/');
    }
  }
}
